local modname = "wechat_random2"
local _M = { _VERSION = '0.0.1' }
_G[modname] = _M

local require = require
local ffi = require "ffi"
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_typeof = ffi.typeof
local C = ffi.C
local type = type
local random = math.random
local randomseed = math.randomseed
local concat = table.concat
local tostring = tostring
local pcall = pcall

ffi.cdef [[
typedef unsigned char u_char;
u_char * ngx_hex_dump(u_char *dst, const u_char *src, size_t len);
int RAND_bytes(u_char *buf, int num);
]]

local ok, new_tab = pcall(require, "table.new")
if not ok then
  new_tab = function()
    return {}
  end
end

local alnum = {
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
}

local t = ffi_typeof "uint8_t[?]"

local function bytes(len, format)
  local s = ffi_new(t, len)
  C.RAND_bytes(s, len)
  if not s then
    return nil
  end
  if format == "hex" then
    local b = ffi_new(t, len * 2)
    C.ngx_hex_dump(b, s, len)
    return ffi_str(b, len * 2), true
  else
    return ffi_str(s, len), true
  end
end

local function seed()
  local a, b, c, d = bytes(4):byte(1, 4)
  return randomseed(a * 0x1000000 + b * 0x10000 + c * 0x100 + d)
end

local function number(min, max, reseed)
  if reseed then
    seed()
  end
  if min and max then
    return random(min, max)
  elseif min then
    return random(min)
  else
    return random()
  end
end
_M.number = number

function _M.token(len, chars, sep)
  chars = chars or alnum
  local count
  local token = new_tab(len, 0)
  if type(chars) ~= "table" then
    chars = tostring(chars)
    count = #chars
    local n
    for i = 1, len do
      n = number(1, count)
      token[i] = chars:sub(n, n)
    end
  else
    count = #chars
    for i = 1, len do
      token[i] = chars[number(1, count)]
    end
  end
  return concat(token, sep)
end

seed()

return _M
